package com.prj.ufdm.cloud.svr.gateway.bpo._impl;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.prj.ufdm.cloud.base.dto.RetData;
import com.prj.ufdm.cloud.svr.gateway.bpo.AuthBPO;
import com.prj.ufdm.cloud.svr.gateway.bpo.SysFunctionBPO;
import com.prj.ufdm.cloud.svr.gateway.bpo.SysUserBPO;
import com.prj.ufdm.cloud.svr.gateway.config.RedisService;
import com.prj.ufdm.cloud.svr.gateway.constant.WebMsUrlConstants;
import com.prj.ufdm.cloud.svr.gateway.constant.WebSecurityConstants;
import com.prj.ufdm.cloud.svr.gateway.enums.GatewayRetCodeEnum;
import com.prj.ufdm.cloud.svr.gateway.model.SysFunction;
import com.prj.ufdm.cloud.svr.gateway.model.SysUser;
import reactor.core.publisher.Mono;

@Service
public class AuthBPOImpl implements AuthBPO {

	@Autowired
	private SysUserBPO sysUserBPO;
	@Autowired
	private SysFunctionBPO sysFunctionBPO;
	@Autowired
	private RedisService redisService;
	
	@Override
	public RetData<SysUser> validateAuthURI(String requestURI, SysUser sysUser) throws Exception {
		RetData<SysUser> retData = new RetData<>();
		List<SysFunction> userSysFunction = null;
		// 如果地址为空,放行
		if(requestURI==null || requestURI.equals("")) {
			return retData;
		}
		try {
			if(WebSecurityConstants.REDIS_ENABLED) {
				Object objUserSysFunction = redisService.hget(WebSecurityConstants.REDIS_MAP_KEY_USERID_FUNCTION, sysUser.getId());
				if(objUserSysFunction!=null) {
					userSysFunction = JSON.parseObject(objUserSysFunction.toString(), new TypeReference<List<SysFunction>>(){}.getType());
				}
			}
			else {
				userSysFunction = sysFunctionBPO.doGetFunctionListByUserId(sysUser.getId());
			}
			if(userSysFunction!=null) {
				for(SysFunction function : userSysFunction) {
		            if(requestURI.equals(function.getFunctionUrl())) {
		            	return retData;
		            }
		        }
			}
		}
		catch(Exception er) {
    		retData.getRetHead().setCode(GatewayRetCodeEnum.RET_CODE_0801010.getCode());
    		retData.getRetHead().setMessage(GatewayRetCodeEnum.RET_CODE_0801010.getMessage());
    		return retData;
		}
		retData.getRetHead().setCode(GatewayRetCodeEnum.RET_CODE_0801007.getCode());
		retData.getRetHead().setMessage(GatewayRetCodeEnum.RET_CODE_0801007.getMessage());
		return retData;
	}


	@Override
	public RetData<SysUser> validateToken(ServerHttpRequest request) throws Exception {
		RetData<SysUser> retData = new RetData<>();
		String headTokenValue = request.getHeaders().getFirst(WebSecurityConstants.AUTH_USER_TOKEN);
		String paramTokenKey = request.getQueryParams().getFirst(WebSecurityConstants.AUTH_USER_TOKEN);
		// 判断TOKEN是否存在
		if( (headTokenValue==null || headTokenValue.equals("")) &&  (paramTokenKey==null || paramTokenKey.equals("")) ) {
    		retData.getRetHead().setCode(GatewayRetCodeEnum.RET_CODE_0801008.getCode());
    		retData.getRetHead().setMessage(GatewayRetCodeEnum.RET_CODE_0801008.getMessage());
    		return retData;
		}
		if(headTokenValue==null || "".equals(headTokenValue)) {
			headTokenValue = paramTokenKey;
		}
		SysUser sysUser = null;
		try {
			if(WebSecurityConstants.REDIS_ENABLED) {
				Object objSysUser = redisService.get(WebSecurityConstants.REDIS_MAP_KEY_TOKEN_USERINFO+":"+headTokenValue);
				if(objSysUser!=null) {
					sysUser = JSON.parseObject(objSysUser.toString(), new TypeReference<SysUser>(){}.getType());
				}
			}
			else {
				sysUser = sysUserBPO.doGetSysUserByToken(headTokenValue);
			}
		}
		catch(Exception er) {
			er.printStackTrace();
    		retData.getRetHead().setCode(GatewayRetCodeEnum.RET_CODE_0801006.getCode());
    		retData.getRetHead().setMessage(GatewayRetCodeEnum.RET_CODE_0801006.getMessage());
    		return retData;
		}
		if(sysUser==null) {
    		retData.getRetHead().setCode(GatewayRetCodeEnum.RET_CODE_0801005.getCode());
    		retData.getRetHead().setMessage(GatewayRetCodeEnum.RET_CODE_0801005.getMessage());
    		return retData;
		}
		retData.setRetBody(sysUser);
		return retData;
	}

	@Override
	public boolean checkAuthURI(String requestURI) throws Exception {
		boolean isAuthURI = false;
		List<SysFunction> metadataSource = null;
		try {
			if(WebSecurityConstants.REDIS_ENABLED) {
				Object objMetaDataSource = redisService.get(WebSecurityConstants.REDIS_MAP_KEY_FUNCTION_METADATASOURCE);
				if(objMetaDataSource!=null) {
					metadataSource = JSON.parseObject(objMetaDataSource.toString(), new TypeReference<List<SysFunction>>(){}.getType());
				}
			}
			else {
				// 从库中获取
				metadataSource = sysFunctionBPO.doGetFunctionList();
			}
			for(SysFunction function : metadataSource) {
	            if(requestURI.equals(function.getFunctionUrl())) {
	            	return true;
	            }
	        }
		}
		catch(Exception er) {
			er.printStackTrace();
		}
		return isAuthURI;
	}
	
	@Override
	public boolean checkAllowURI(String url) throws Exception {
		boolean matchResult = false;
		PathMatcher matcher = new AntPathMatcher();
		String [] arrPattern =  WebSecurityConstants.AUTH_ALLLOW_URL.split(","); 
		if(arrPattern!=null) {
			for(String patternPath:arrPattern) {
				if(matcher.match(patternPath, url)) {
					matchResult = true;
					break;
				}
			}
		}
		return matchResult;
	}
	
	@Override
	public Mono<Void> exchangeHandle(ServerWebExchange exchange, GatewayFilterChain chain, SysUser sysUser) throws Exception{
		ServerHttpRequest request =  exchange.getRequest();
		if (request.getMethod() == HttpMethod.POST || request.getMethod() == HttpMethod.GET ) {
			ServerHttpRequest newrequest = request.mutate().headers(httpHeaders -> {
				httpHeaders.add(WebSecurityConstants.AUTH_USER_ID, sysUser.getId());
				httpHeaders.add(WebSecurityConstants.AUTH_USER_NAME, sysUser.getUsername()!=null ? sysUser.getUsername() : "");
				try {
					httpHeaders.add(WebSecurityConstants.AUTH_REAL_NAME, java.net.URLEncoder.encode(sysUser.getRealName(), "utf-8"));
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				httpHeaders.add(WebSecurityConstants.AUTH_USER_ORGAN_ID, sysUser.getOrganId()!=null ?  sysUser.getOrganId() : "");
				httpHeaders.add(WebSecurityConstants.AUTH_USER_ORGAN_CODE, sysUser.getOrganCode()!=null ? sysUser.getOrganCode() : "");
				httpHeaders.add(WebSecurityConstants.AUTH_USER_GRANT_ORGAN_IDS, sysUser.getGrantOrganIds()!=null ? sysUser.getGrantOrganIds() : "");
			}).build();
			ServerWebExchange build = exchange.mutate().request(newrequest).build();
			return chain.filter(build);
		}
        return chain.filter(exchange);
	}

	@Override
	public void setMetadadaSource(String requestURI) throws Exception {
		
		if(WebSecurityConstants.REDIS_ENABLED) {
			if( requestURI.equals(WebMsUrlConstants.METASOURCE_ADD_URL) 
				|| requestURI.equals(WebMsUrlConstants.METASOURCE_UPDATE_URL)
				|| requestURI.equals(WebMsUrlConstants.METASOURCE_DEL_URL)
				|| requestURI.equals(WebMsUrlConstants.METASOURCE_GET_URL) ) {
				List<SysFunction> listSysFunction = sysFunctionBPO.doGetFunctionList();
				redisService.set(WebSecurityConstants.REDIS_MAP_KEY_FUNCTION_METADATASOURCE, listSysFunction);
			}
		}
	}

	@Override
	public Mono<Void> exchangeErrHandle(ServerHttpResponse response, RetData<?> retData){
		byte[] dataBytes = JSON.toJSONString(retData).getBytes();
		DataBuffer bodyDataBuffer = response.bufferFactory().wrap(dataBytes);
		response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
		return response.writeWith(Mono.just(bodyDataBuffer));
	}

}
